

//
//  XYXSQLiteManager.swift
//  数据库
//
//  Created by 亚杏 on 2017/6/14.
//  Copyright © 2017年 oms. All rights reserved.
//

import Foundation
import FMDB


//最大的数据库缓存时间
private let maxDBCacheTime: TimeInterval = -60 //(-5) * 24 * 60 * 60

//SQLite管理器
/**
 1. 数据库本质上是保存在沙盒中的一个文件，首先需要创建并打开数据库
 2. 创建数据表
 3. 增删改查

 **/

class XYXSQLiteManager {

    //单例，全局数据库工具访问点
    static let shared = XYXSQLiteManager()

    //数据库队列
    let queue: FMDatabaseQueue


    //构造函数
    private init() {

        //数据库的全路径 - path
        let dbName = "status.db"
        var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        path = (path as NSString).appendingPathComponent(dbName)
        print("数据库路径\(path)")

        
        //创建数据库队列,同时’创建或打开’数据库
        queue = FMDatabaseQueue(path: path)

        //打开数据库
        createTable()

        //注册通知
        NotificationCenter.default.addObserver(self, selector: #selector(clearDBCache), name: NSNotification.Name.UIApplicationDidEnterBackground, object: nil)

    }


    // - SQLite 的数据不断地增加数据，数据库文件大小会不断增加
    // - 如果删除了数据，数据库的不会变小
    // - 想让数据库变小
    /**
     1> 将数据库文件复制一个新的副本，status.db.old
     2> 新建一个空的数据库文件
     3> 自己编写 SQL，从 old 中将所有的数据读出，写入新的数据库
     **/
    @objc private func clearDBCache() {

        //获得距离现在日期delta天的日期
        let dateString = Date.xyx_dateString(delta: maxDBCacheTime)

        print("清理缓存\(dateString)")

        //准备SQL
        let sql = "DELETE FROM T_Status WHERE createTime < ?;"

        //执行SQL
        queue.inDatabase { (db) in
            if db?.executeUpdate(sql, withArgumentsIn: [dateString]) == true {
                print("删除\(String(describing: db?.changes()))条数据")
            }
        }

    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    //创建数据表
  private func createTable() {

        //1.sql
        guard let path = Bundle.main.path(forResource: "status.sql", ofType: nil),
            let sql = try? String(contentsOfFile: path)
            else{
            return
        }

        //2. 执行sql - FMDB的内部队列是串行列队，同步执行
        // 可以保证同一时间只有一个任务操作数据库，保证其读写安全
        queue.inDatabase { (db) in

            // 只在创表时才使用statements，可以一次创建多个数据表
            // 在执行增删改的时候，一定不要使用statements方法，否则可能被注入
            if db?.executeStatements(sql) == true {
                print("建表成功")
            } else {
                print("建表失败")
            }
        }

        print("over")
    }

}

//MARK:微博数据操作
extension XYXSQLiteManager {


    /// 从数据库取出微博数据
    ///
    /// - Parameters:
    ///   - userId: 当前登录的用户帐号
    ///   - since_id: 上拉的数据
    ///   - max_id: 下拉的数据
    /// - Returns: 微博的字典的数组，将数据库中的 status 字段对应的二进制数据反序列化，生成字典
    func loadStatus(userid: String, since_id: Int64 = 0, max_id: Int64 = 0 ) -> [[String: AnyObject]] {

        //1.准备SQL
        var sql = "SELECT statusid, userid, status FROM T_Status \n"
        sql += "WHERE userid = \(userid) \n"

        //上拉下拉处理
        if since_id > 0 {//下拉
            sql += "AND statusid > \(since_id)\n"
        } else if max_id > 0 {//上拉
            sql += "AND statusid < \(max_id)\n"
        }
        sql += "ORDER BY statusid DESC LIMIT 20;"

        //拼接 SQL 结束后，一定要测试
        print(sql)

        //2.执行SQL
        let array = execRebackSet(sql: sql)

        //3,遍历数组，将数组中的status反序列化 -> 字典数组
        var result = [[String: AnyObject]]()
        for dict in array {
            guard let jsonData = dict["status"] as? Data,
            let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: AnyObject]
                else
            {
                continue
            }

            //追加到数组
            result.append(json ?? [:])

        }
        

        return result
    }


    ///新增或修改微博数据，微博在刷新时，可能会出现重叠
   // 请求下来的数据 是一个字典数组，包含了statusId,没有userId

    ///   - userId: 当前登录用户的id
    ///   - array: 从网络获取的‘字典数组’
    func updateStatus(userId: String, array:[[String: AnyObject]]) {

        //1.准备SQL
        /**
         statusId: 要保存的微博代号
         userId:   当前登录用户的id
         status:   完整微博字典的json二进制数据
         */
        let sql = "INSERT OR REPLACE INTO T_Status (statusId, userId, status) VALUES (?, ?, ?)"

        //执行SQL,开启事务
        queue.inTransaction { (db, rollback) in

            //遍历数组，逐条插入微博数据
            for dict in array {

                //将字典序列化成二进制数据

              guard let statusId = dict["idstr"] as? String,
                let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: []) else{
                    continue
                }

                //执行SQL
                if db?.executeUpdate(sql, withArgumentsIn: [statusId, userId, jsonData]) == false {
                    //FIXME
                    //需要回滚
                    rollback?.pointee = true
                    break
                }

            }
        }
    }

}


//查询
extension XYXSQLiteManager {
    /// 执行一个 SQL， 返回字典数组
    /// - Parameter sql: sql
    /// - Returns: 字典数组
    func execRebackSet(sql:String) -> [[String: AnyObject]] {

        var result = [[String: AnyObject]]()

        //执行SQL - 查询数据，不会修改数据，所以不需要开启事务
        //事务的目的，是为了保证数据的有效性，一旦失败，回滚到初始状态
        queue.inDatabase { (db) in
            guard let rs =  db?.executeQuery(sql, withArgumentsIn: []) else {
                return
            }
            //遍历结果集合
            while rs.next() {

                //1.列数
                let columnCount = rs.columnCount()
                //2.遍历所有列
                for col in 0..<columnCount {

                    //3.列名 - key
                  guard let name = rs.columnName(for: col),
                    //4.值 - value
                    let value = rs.object(forColumnIndex: col) else{
                        return
                    }

                    //追加结果
                    result.append([name: value as AnyObject])

                }
            }
        }

        return result

}

}
